package core.util;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ArrayUtil {

	public static boolean isEmpty(Object[] array) {
		return (array == null || array.length == 0);
	}

	public static boolean isNotEmpty(Object[] array) {
		return (array != null && array.length > 0);
	}

	public static boolean contains(Object[] array, Object target) {
		if (!isEmpty(array)) {
			for (Object obj : array) {
				if ((target == null && obj == null) || (target != null && target.equals(obj))) {
					return true;
				}
			}
		}

		return false;
	}
	
	public static <T> T getElement(T[] array, int index) {
		if (array == null || index < 0 || index >= array.length) {
			return null;
		}

		return array[index];
	}

	public static <T> T[][] split(T[] array, int length) {
		return split(array, length, false);
	}

	public static <T> T[][] split(T[] array, int length, boolean isKeepSameLength) {
		if (array == null) {
			return null;
			
		} else if (array.length == 0) {
			return newArray(array, new int[] { 1, 0 });
		}

		if (length <= 0) {
			length = array.length;
		}

		T[][] result = newArray(array, new int[] { (int) Math.ceil((double) array.length / length), 0 });
		for (int ai = 0, ri = 0, riLen = length; ai < array.length; ai += length, ri++) {
			if (array.length - ai < length) {
				riLen = array.length - ai;
			}

			result[ri] = newArray(array, isKeepSameLength ? length : riLen);
			System.arraycopy(array, ai, (Object[]) result[ri], 0, riLen);
		}

		return result;
	}

	@SuppressWarnings("unchecked")
	public static <T> T newArray(Object reference, int dimension) {
		return (T) newArray(reference, new int[] { dimension });
	}

	@SuppressWarnings("unchecked")
	public static <T> T newArray(Object reference, int[] dimensions) {
		Class<?> clazz = null;
		if (reference instanceof Class<?>) {
			clazz = (Class<?>) reference;
			
		} else if (reference != null){
			Class<?> refType = reference.getClass();
			if (refType.isArray()) {
				clazz = refType.getComponentType();
				
			} else {
				clazz = refType;
			}
		}
		
		if (clazz != null) {
			try {
				return (T) Array.newInstance(clazz, dimensions);
			} catch (Exception e) {
			}
		}
		
		return null;
	}

	public static <T> T[] asArray(T... objs) {
		return objs;
	}

	public static <T> List<T> asList(T... objs) {
		if (objs == null) {
			return null;
		}

		List<T> list = new ArrayList<T>();
		for (T obj : objs) {
			list.add(obj);
		}

		return list;
	}

	public static <T> Set<T> asSet(T... objs) {
		if (objs == null) {
			return null;
		}

		Set<T> set = new LinkedHashSet<T>();
		for (T obj : objs) {
			set.add(obj);
		}

		return set;
	}

	public static <T> Map<T, T> asMap(T[][] objs) {
		return asMultiTypeMap(objs);
	}

	@SuppressWarnings("unchecked")
	public static <K, V, T> Map<K, V> asMultiTypeMap(T[][] objs) {
		if (isEmpty(objs)) {
			return Collections.emptyMap();
		}

		Map<K, V> map = new LinkedHashMap<K, V>();
		for (T[] obj : objs) {
			if (obj != null && obj.length >= 2) {
				map.put((K) obj[0], (V) obj[1]);
			}
		}

		return map;
	}

	public static <T> Map<T, T> asMap(T... objs) {
		return asMultiTypeMap(objs);
	}

	@SuppressWarnings("unchecked")
	public static <K, V, T> Map<K, V> asMultiTypeMap(T... objs) {
		if (isEmpty(objs)) {
			return Collections.emptyMap();
		}

		Map<K, V> map = new LinkedHashMap<K, V>();
		for (int i = 0; i < objs.length;) {
			map.put((K) objs[i++], (V) getElement(objs, i++));
		}

		return map;
	}

	@SuppressWarnings("unchecked")
	public static <T> T[] match(T[] array, Matcher<T> matcher) {
		if (matcher == null) {
			return null;
		}
		
		return matchAll(array, matcher);
	}

	public static <T> T[] matchAny(T[] array, Matcher<T>... matchers) {
		return match(array, matchers, false);
	}

	public static <T> T[] matchAll(T[] array, Matcher<T>... matchers) {
		return match(array, matchers, true);
	}

	private static <T> T[] match(T[] array, Matcher<T>[] matchers, boolean isMatchAll) {
		if (isEmpty(array) || isEmpty(matchers)) {
			return null;
		}

		List<T> matched = new ArrayList<T>();
		for (T obj : array) {
			if (MatchHelper.match(obj, matchers, isMatchAll)) {
				matched.add(obj);
			}
		}

		T[] result = newArray(array, new int[] { matched.size() });
		return matched.toArray(result);
	}
	
	private ArrayUtil() {
	}
}
